/* See the file "LICENSE" for the full license governing this code. */
package com.plugandcare.graphyty.gui;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.achartengine.ChartFactory;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Paint.Align;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;

import com.plugandcare.data.VitalSignProviderContants;
import com.plugandcare.graphyty.R;

/**
 * Base class for all activities showing graphs.
 * 
 * @author Doreen Seider
 * @author Steven Mohr
 * 
 */
public abstract class AbstractChartActivity extends Activity {

    protected static final String LOG_TAG = "PNCCGraph";

    private static final String NEWDATA_ACTION = "com.plugandcare.NEWDATA";

    private static final String ACTION_SELECT_DEVICE = "com.plugandcare.queryDevice";

    private final BroadcastReceiver bcReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            refresh();
            Toast.makeText(context, getString(R.string.toast_newData), Toast.LENGTH_LONG).show();
        }
    };
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.graph);
    }

    @Override
    public void onResume() {
        super.onResume();
        registerReceiver(bcReceiver, new IntentFilter(NEWDATA_ACTION));
        refresh();
    }
    
    @Override
    public void onStart() {
        super.onStart();
        refresh();
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterReceiver(bcReceiver);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.graph, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        boolean handled = false;
        switch (item.getItemId()) {
        case R.id.about:
            showAboutDialog();
            handled = true;
            break;
        case R.id.query:
            startQueryActivityOfPncc();
            handled = true;
            break;
        case R.id.refresh:
            refresh();
            handled = true;
            break;
        default:
            handled = super.onOptionsItemSelected(item);
            break;
        }
        return handled;
    }

    /**
     * Tells the Plug&Care Connector to query new measurement data from a medical device.
     */
    public void startQueryActivityOfPncc() {
        if (isIntentAvailable(this, ACTION_SELECT_DEVICE)) {
            startActivityForResult(new Intent(ACTION_SELECT_DEVICE), 0);
        } else {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle(getString(R.string.dialog_pnccnotinstalled_title))
                .setMessage(getString(R.string.dialog_pnccnotinstalled_body))
                .setNeutralButton(getApplicationContext().getString(R.string.ok), new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                    }
                });
            builder.create().show();
        }
    }

    protected double[][] getHoursAndMinutes(ChartType chartType) {
        ContentResolver cr = getContentResolver();
        Cursor c = null;
        double[][] hoursAndMinutes = null;

        switch (chartType) {
        case BLOOD_GLUCOSE:
            c = cr.query(VitalSignProviderContants.BloodGlucoseColumns.CONTENT_URI,
                VitalSignProviderContants.BloodGlucoseColumns.COLUMN_NAMES, null, null, null);
            break;
        case BODY_WEIGHT:
            c = cr.query(VitalSignProviderContants.BodyWeightColumns.CONTENT_URI, VitalSignProviderContants.BodyWeightColumns.COLUMN_NAMES,
                null, null, null);
            break;

        case BLOOD_PRESSURE:
            c = cr.query(VitalSignProviderContants.BloodPressureColumns.CONTENT_URI,
                VitalSignProviderContants.BloodPressureColumns.COLUMN_NAMES, null, null, null);
            break;
        default:
            break;
        }

        if (c != null) {
            hoursAndMinutes = new double[2][c.getCount()];
            c.moveToFirst();

            for (int i = 0; i < c.getCount(); i++) {
                hoursAndMinutes[0][i] = Double.parseDouble(new SimpleDateFormat("HH").format(c.getLong(c.getColumnIndex("TIMESTAMP"))));
                double minutes = Double.parseDouble(new SimpleDateFormat("mm").format(c.getLong(c.getColumnIndex("TIMESTAMP"))));
                hoursAndMinutes[1][i] = minutes / 60;
                c.moveToNext();
            }
            for (int i = 0; i < hoursAndMinutes[0].length - 1; i++) {
                if (hoursAndMinutes[0][i] > hoursAndMinutes[0][i + 1]) {
                    double mark = hoursAndMinutes[0][i];
                    hoursAndMinutes[0][i] = hoursAndMinutes[0][i + 1];
                    hoursAndMinutes[0][i + 1] = mark;
                    mark = hoursAndMinutes[1][i];
                    hoursAndMinutes[1][i] = hoursAndMinutes[1][i + 1];
                    hoursAndMinutes[1][i + 1] = mark;
                }
            }
        }
        return hoursAndMinutes;
    }

    private void showAboutDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(getApplicationContext().getString(R.string.dialog_about_title))
            .setMessage(getApplicationContext().getString(R.string.dialog_about_body))
            .setNeutralButton(getApplicationContext().getString(R.string.ok), new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int id) {
                    dialog.cancel();
                }
            });
        builder.create().show();
    }

    protected XYMultipleSeriesDataset buildDataset(String[] titles, List<double[]> xValues,
        List<double[]> yValues) {
        XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
        addXYSeries(dataset, titles, xValues, yValues, 0);
        return dataset;
    }

    protected void addXYSeries(XYMultipleSeriesDataset dataset, String[] titles, List<double[]> xValues,
        List<double[]> yValues, int scale) {
        int length = titles.length;
        for (int i = 0; i < length; i++) {
            XYSeries series = new XYSeries(titles[i], scale);
            double[] xV = xValues.get(i);
            double[] yV = yValues.get(i);
            int seriesLength = xV.length;
            for (int k = 0; k < seriesLength; k++) {
                series.add(xV[k], yV[k]);
            }
            dataset.addSeries(series);
        }
    }

    protected void setChartSettings(XYMultipleSeriesRenderer renderer, String title, String xTitle,
        String yTitle, double xMin, double xMax, double yMin, double yMax, int axesColor,
        int labelsColor) {
        renderer.setChartTitle(title);
        renderer.setXTitle(xTitle);
        renderer.setYTitle(yTitle);
        renderer.setXAxisMin(xMin);
        renderer.setXAxisMax(xMax);
        renderer.setYAxisMin(yMin);
        renderer.setYAxisMax(yMax);
        renderer.setAxesColor(axesColor);
        renderer.setLabelsColor(labelsColor);
    }

    private boolean isIntentAvailable(Context context, String action) {
        final PackageManager packageManager = context.getPackageManager();
        final Intent intent = new Intent(action);
        List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        if (resolveInfo.size() > 0) {
            return true;
        }
        return false;
    }

    protected XYMultipleSeriesRenderer createRenderer(int numberOfDatasets) {

        XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(numberOfDatasets);
        renderer.setAxisTitleTextSize(12);
        renderer.setChartTitleTextSize(20);
        renderer.setLabelsTextSize(12);
        renderer.setLegendTextSize(15);
        renderer.setPointSize(3f);
        renderer.setMargins(new int[] { 30, 40, 20, 30 });

        int[] colors = { Color.RED, Color.BLUE, Color.GREEN };
        PointStyle[] pointStyles = { PointStyle.CIRCLE, PointStyle.TRIANGLE, PointStyle.DIAMOND };
        XYSeriesRenderer[] r = new XYSeriesRenderer[3];

        for (int i = 0; i < numberOfDatasets; i++) {
            r[i] = new XYSeriesRenderer();
            r[i].setColor(colors[i]);
            r[i].setPointStyle(pointStyles[i]);
            renderer.addSeriesRenderer(r[i]);

        }

        for (int i = 0; i < renderer.getSeriesRendererCount(); i++) {
            ((XYSeriesRenderer) renderer.getSeriesRendererAt(i)).setFillPoints(true);
        }

        renderer.setXLabels(12);
        renderer.setYLabels(10);
        renderer.setShowGrid(true);
        renderer.setXLabelsAlign(Align.RIGHT);
        renderer.setYLabelsAlign(Align.RIGHT);
        renderer.setZoomButtonsVisible(false);
        renderer.setZoomEnabled(false, false);
        renderer.setPanEnabled(false, false);

        return renderer;
    }

    protected void createEmptyChart(ChartType chartType) {

        XYMultipleSeriesRenderer renderer = null;
        XYMultipleSeriesDataset dataset = null;
        List<double[]> x = new ArrayList<double[]>();
        x.add(new double[] { 0 });
        List<double[]> y = new ArrayList<double[]>();
        y.add(new double[] { 0 });

        renderer = createRenderer(1);

        switch (chartType) {
        case BLOOD_GLUCOSE:
            dataset = buildDataset(new String[] { getString(R.string.blood_glucose) }, x, y);
            setChartSettings(renderer, getString(R.string.blood_glucose), getString(R.string.time), getString(R.string.unit_mgdl), 0, 12,
                0, 200, Color.LTGRAY, Color.LTGRAY);
            break;
        case BODY_WEIGHT:
            dataset = buildDataset(new String[] { getString(R.string.body_weight) }, x, y);
            setChartSettings(renderer, getString(R.string.body_weight), getString(R.string.time), getString(R.string.unit_kg), 0, 12, 0,
                150, Color.LTGRAY, Color.LTGRAY);
            break;
        case BLOOD_PRESSURE:
            dataset = buildDataset(new String[] { getString(R.string.blood_pressure) }, x, y);
            setChartSettings(renderer, getString(R.string.blood_pressure), getString(R.string.time), getString(R.string.unit_mmhg), 0, 12,
                0, 200, Color.LTGRAY, Color.LTGRAY);
            break;
        default:
            break;
        }

        setContentView(ChartFactory.getLineChartView(this, dataset, renderer));
    }

    protected abstract void refresh();

    protected double getMax(double[] array) {
        double max = array[0];

        for (int i = 0; i < array.length; i++) {
            if (max < array[i]) {
                max = array[i];
            }
        }
        return max;
    }

    protected double getMin(double[] array) {
        double min = array[0];

        for (int i = 0; i < array.length; i++) {
            if (min > array[i]) {
                min = array[i];
            }
        }
        return min;
    }

    /**
     * Defines what kind of chart we are dealing with.
     * 
     * @author Steven Mohr
     * 
     */
    public enum ChartType {
        BLOOD_PRESSURE, BLOOD_GLUCOSE, BODY_WEIGHT
    }
}
